package com.occludens.admin.datamodel;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Cache;
import com.googlecode.objectify.annotation.Load;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.annotation.Subclass;

@Subclass(index=true)
@Cache
public class Relationship extends AdminObjectDataModel {
	@Parent private Ref<Relationship> derived;
	@Load private List<Ref<Attribute>> attributes = new ArrayList<Ref<Attribute>>();
	@Load private List<Ref<Trigger>> triggers = new ArrayList<Ref<Trigger>>();
	@Load private List<Ref<Type>> fromTypes = new ArrayList<Ref<Type>>();
	@Load private List<Ref<Type>> toTypes = new ArrayList<Ref<Type>>();
	@Load private List<Ref<Relationship>> fromRelationships = new ArrayList<Ref<Relationship>>();
	@Load private List<Ref<Relationship>> toRelationships = new ArrayList<Ref<Relationship>>();
	protected Relationship() {} // Mandatory for Objectify
	
	public Relationship(String name) {
		super(name);
	}
	
	public Relationship getDerived() {
		return this.derived.get();
	}
	
	public void setDerived (Relationship relationship) {
		this.derived = Ref.create(relationship);
	}
	
	public List<Attribute> getAttributes() {
		List<Attribute> returnList = new ArrayList<Attribute>();
		Iterator<Ref<Attribute>> attributesItr = this.attributes.iterator();
		while (attributesItr.hasNext()) {
			returnList.add(attributesItr.next().get());
		}//End of while attributesItr
		return returnList;
	}

	public void setAttributes(List<Attribute> attributes) {
		Iterator<Attribute> attributesItr = attributes.iterator();
		while (attributesItr.hasNext()) {
			Ref<Attribute> attributeRef = Ref.create(attributesItr.next());
			if (!this.attributes.contains(attributeRef)) {
				this.attributes.add(attributeRef);
			}
		}//End of while attributesItr
	}

	public void addAttributes(Attribute... attributes) {
		for (int i=0; i<attributes.length; i++) {
			Ref<Attribute> attributeRef = Ref.create(attributes[i]);
			if (!this.attributes.contains(attributeRef)) {
				this.attributes.add(attributeRef);
			}
		}
	}

	public void removeAttributes(Attribute... attributes) {
		for (int i=0; i<attributes.length; i++) {
			Ref<Attribute> attributeRef = Ref.create(attributes[i]);
			if (this.attributes.contains(attributeRef)) {
				this.attributes.remove(attributeRef);
			}
		}
	}
	
	public List<Trigger> getTriggers() {
		List<Trigger> returnList = new ArrayList<Trigger>();
		Iterator<Ref<Trigger>> triggersItr = this.triggers.iterator();
		while (triggersItr.hasNext()) {
			returnList.add(triggersItr.next().get());
		}//End of while triggersItr
		return returnList;
	}

	public void setTriggers(List<Trigger> triggers) {
		Iterator<Trigger> triggersItr = triggers.iterator();
		while (triggersItr.hasNext()) {
			Ref<Trigger> triggerRef = Ref.create(triggersItr.next());
			if (!this.triggers.contains(triggerRef)) {
				this.triggers.add(triggerRef);
			}
		}//End of while attributesItr
	}

	public void addTriggers(Trigger... triggers) {
		for (int i=0; i<triggers.length; i++) {
			Ref<Trigger> triggerRef = Ref.create(triggers[i]);
			if (!this.triggers.contains(triggerRef)) {
				this.triggers.add(triggerRef);
			}
		}
	}

	public void removeTriggers(Trigger... triggers) {
		for (int i=0; i<triggers.length; i++) {
			Ref<Trigger> triggerRef = Ref.create(triggers[i]);
			if (this.triggers.contains(triggerRef)) {
				this.triggers.remove(triggerRef);
			}
		}
	}
	
	public List<Type> getFromTypes() {
		return this.getTypes(this.fromTypes);
	}
	
	public List<Type> getToTypes() {
		return this.getTypes(this.toTypes);
	}
	
	private List<Type> getTypes(List<Ref<Type>> directionTypes) {
		List<Type> returnList = new ArrayList<Type>();
		Iterator<Ref<Type>> directionTypesItr = directionTypes.iterator();
		while (directionTypesItr.hasNext()) {
			returnList.add(directionTypesItr.next().get());
		}//End of while directionTypesItr
		return returnList;
	}
	
	public void setFromTypes(List<Type> types) {
		this.setTypes(this.fromTypes, types);
	}
	
	public void setToTypes(List<Type> types) {
		this.setTypes(this.toTypes, types);
	}

	private void setTypes(List<Ref<Type>> directionTypes, List<Type> types) {
		Iterator<Type> typesItr = types.iterator();
		while (typesItr.hasNext()) {
			Ref<Type> typeRef = Ref.create(typesItr.next());
			if (!directionTypes.contains(typeRef)) {
				directionTypes.add(typeRef);
			}
		}//End of while typesItr
	}
	
	public void addFromTypes(Type... types) {
		this.addTypes(this.fromTypes, types);
	}
	
	public void addToTypes(Type... types) {
		this.addTypes(this.toTypes, types);
	}

	private void addTypes(List<Ref<Type>> directionTypes, Type... types) {
		for (int i=0; i<types.length; i++) {
			Ref<Type> typeRef = Ref.create(types[i]);
			if (!directionTypes.contains(typeRef)) {
				directionTypes.add(typeRef);
			}
		}
	}
	
	public void removeFromType(Type... types) {
		this.removeTypes(this.fromTypes, types);
	}
	
	public void removeToTypes(Type... types) {
		this.removeTypes(this.toTypes, types);
	}

	private void removeTypes(List<Ref<Type>> directionTypes, Type... types) {
		for (int i=0; i<types.length; i++) {
			Ref<Type> typeRef = Ref.create(types[i]);
			if (directionTypes.contains(typeRef)) {
				directionTypes.remove(typeRef);
			}
		}
	}
	
	public List<Relationship> getFromRelationships() {
		return this.getRelationships(this.fromRelationships);
	}
	
	public List<Type> getToRelationships() {
		return this.getTypes(this.toTypes);
	}
	
	private List<Relationship> getRelationships(List<Ref<Relationship>> directionRelationships) {
		List<Relationship> returnList = new ArrayList<Relationship>();
		Iterator<Ref<Relationship>> directionRelationshipsItr = directionRelationships.iterator();
		while (directionRelationshipsItr.hasNext()) {
			returnList.add(directionRelationshipsItr.next().get());
		}//End of while directionRelationshipsItr
		return returnList;
	}
	
	public void setFromRelationships(List<Relationship> relationships) {
		this.setRelationships(this.fromRelationships, relationships);
	}
	
	public void setToRelationships(List<Relationship> relationships) {
		this.setRelationships(this.toRelationships, relationships);
	}

	private void setRelationships(List<Ref<Relationship>> directionRelationships, List<Relationship> relationships) {
		Iterator<Relationship> relationshipsItr = relationships.iterator();
		while (relationshipsItr.hasNext()) {
			Ref<Relationship> relationshipRef = Ref.create(relationshipsItr.next());
			if (!directionRelationships.contains(relationshipRef)) {
				directionRelationships.add(relationshipRef);
			}
		}//End of while relationshipsItr
	}
	
	public void addFromRelationships(Relationship... relationships) {
		this.addRelationships(this.fromRelationships, relationships);
	}
	
	public void addToRelationships(Relationship... relationships) {
		this.addRelationships(this.toRelationships, relationships);
	}

	private void addRelationships(List<Ref<Relationship>> directionRelationships, Relationship... relationships) {
		for (int i=0; i<relationships.length; i++) {
			Ref<Relationship> relationshipRef = Ref.create(relationships[i]);
			if (!directionRelationships.contains(relationshipRef)) {
				directionRelationships.add(relationshipRef);
			}
		}
	}
	
	public void removeFromRelationships(Relationship... relationships) {
		this.removeRelationships(this.fromRelationships, relationships);
	}
	
	public void removeToRelationships(Relationship... relationships) {
		this.removeRelationships(this.toRelationships, relationships);
	}

	private void removeRelationships(List<Ref<Relationship>> directionRelationships, Relationship... relationships) {
		for (int i=0; i<relationships.length; i++) {
			Ref<Relationship> relationshipRef = Ref.create(relationships[i]);
			if (directionRelationships.contains(relationshipRef)) {
				directionRelationships.remove(relationshipRef);
			}
		}
	}
}
